﻿using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;

namespace YArchitech.Revit
{
	public class ElementNode
	{
		public ElementNode(Element element)
		{
			this.elem = element;
			this.type = this.CheckType(this.elem);
			Connector conn = null;
			ConnectorSetIterator csi = this.GetCSI();
			while (csi.MoveNext())
			{
				object obj = csi.Current;
				Connector connector = obj as Connector;
				if (connector != null && !connector.IsConnected)
				{
					conn = connector;
					break;
				}
			}
			this.InitNode(conn);
		}

		public ElementNode(Element element, Connector connector)
		{
			this.elem = element;
			this.type = this.CheckType(this.elem);
			this.InitNode(connector);
		}

		private void InitNode(Connector conn)
		{
			this.elemConnectorCount = this.GetElementConnectorCount();
			this.connectorIn = conn;
			ConnectorSetIterator csi = this.GetCSI();
			while (csi.MoveNext())
			{
				object obj = csi.Current;
				Connector connector = obj as Connector;
				if (connector != this.connectorIn)
				{
					if (this.type != Autodesk.Revit.DB.ElementType.Wire)
					{
						if (connector != null && connector.Domain != 1 && connector.Domain != 3 && connector.Domain != 4)
						{
							continue;
						}
					}
					else if (this.type == Autodesk.Revit.DB.ElementType.Wire && connector != null && connector.Domain != 2)
					{
						continue;
					}
					if (((connector != null && (int)connector.ConnectorType == 1) || (int)connector.ConnectorType == 2 || (int)connector.ConnectorType == 19) && (this.connectorIn == null || !this.TwoConnectorIsSame(this.connectorIn, connector)) && !this.CheckConnector(connector))
					{
						if (this.subConnectors == null)
						{
							this.subConnectors = new List<Connector>();
							this.subElementNode = new List<ElementNode>();
						}
						this.subConnectors.Add(connector);
					}
				}
			}
		}

		private bool CheckConnector(Connector connector)
		{
			bool result = false;
			if ((int)connector.ConnectorType == 1)
			{
				if (this.type == Autodesk.Revit.DB.ElementType.Duct || this.type == Autodesk.Revit.DB.ElementType.Pipe || this.type == Autodesk.Revit.DB.ElementType.FlexDuct || this.type == Autodesk.Revit.DB.ElementType.FlexPipe || this.type == Autodesk.Revit.DB.ElementType.CableTray || this.type == Autodesk.Revit.DB.ElementType.Conduit || this.type == Autodesk.Revit.DB.ElementType.Wire)
				{
					if (this.connectorIn == null)
					{
						this.connectorIn = connector;
					}
					else if (this.elemConnectorCount < 3)
					{
						if (!this.connectorIn.Origin.IsAlmostEqualTo(connector.Origin))
						{
							this.connectorOut = connector;
							result = true;
						}
					}
					else if (this.elemConnectorCount >= 3 && this.connectorOut == null && !this.TwoConnectorIsSame(this.connectorIn, connector))
					{
						this.connectorOut = connector;
						result = true;
					}
				}
				else if (this.type == Autodesk.Revit.DB.ElementType.FamilyInstance || this.type == Autodesk.Revit.DB.ElementType.Equiment)
				{
					if (this.connectorIn == null)
					{
						this.connectorIn = connector;
					}
					else if (!this.connectorIn.Origin.IsAlmostEqualTo(connector.Origin))
					{
						if (this.elemConnectorCount < 3)
						{
							this.connectorOut = connector;
							result = true;
						}
						else if (this.elemConnectorCount == 3)
						{
							if (this.subConnectors != null && this.subConnectors.Count > 0)
							{
								if (this.subConnectors.ElementAt(0).GetAngleTo(this.connectorIn) >= connector.GetAngleTo(this.connectorIn))
								{
									this.connectorOut = this.subConnectors.ElementAt(0);
									this.subConnectors.RemoveAt(0);
								}
								else if (this.connectorOut == null)
								{
									this.connectorOut = connector;
									result = true;
								}
							}
						}
						else if (this.elemConnectorCount == 4 && this.ConnectorInIsParallel(connector) && this.connectorOut == null)
						{
							this.connectorOut = connector;
							result = true;
						}
					}
				}
			}
			return result;
		}

		private bool TwoConnectorIsSame(Connector conn1, Connector conn2)
		{
			bool result = false;
			if (conn1.Origin.IsAlmostEqualTo(conn2.Origin) && (int)conn1.ConnectorType == (int)conn2.ConnectorType && ((int)conn1.ConnectorType == 1 || (int)conn1.ConnectorType == 2 || (int)conn1.ConnectorType == 19) && ((int)conn2.ConnectorType == 1 || (int)conn2.ConnectorType == 2 || (int)conn2.ConnectorType == 19))
			{
				ConnectorSetIterator connectorSetIterator = conn1.AllRefs.ForwardIterator();
				ConnectorSetIterator connectorSetIterator2 = conn2.AllRefs.ForwardIterator();
				while (connectorSetIterator.MoveNext())
				{
					object obj = connectorSetIterator.Current;
					Connector connector = obj as Connector;
					connectorSetIterator2.Reset();
					while (connectorSetIterator2.MoveNext())
					{
						object obj2 = connectorSetIterator2.Current;
						Connector connector2 = obj2 as Connector;
						if (((int)connector.ConnectorType == 1 || (int)connector.ConnectorType == 2 || (int)connector.ConnectorType == 19) && ((int)connector2.ConnectorType == 1 || (int)connector2.ConnectorType == 2 || (int)connector2.ConnectorType == 19) && connector.Origin.IsAlmostEqualTo(connector2.Origin))
						{
							result = true;
						}
					}
				}
			}
			return result;
		}

		private bool ConnectorInIsOnLine(Connector connector)
		{
			bool result = false;
			if (connector != null && this.elem is FamilyInstance)
			{
				XYZ origin = this.connectorIn.Origin;
				XYZ point = (this.elem.Location as LocationPoint).Point;
				XYZ origin2 = connector.Origin;
				XYZ xyz = point - origin;
				XYZ xyz2 = origin2 - point;
				if (xyz.Normalize().IsAlmostEqualTo(xyz2.Normalize()))
				{
					result = true;
				}
			}
			return result;
		}

		private bool ConnectorInIsParallel(Connector connector)
		{
			bool result = false;
			if (connector != null && this.elem is FamilyInstance && (connector.CoordinateSystem.BasisZ.IsAlmostEqualTo(this.connectorIn.CoordinateSystem.BasisZ) || connector.CoordinateSystem.BasisZ.IsAlmostEqualTo(-this.connectorIn.CoordinateSystem.BasisZ)))
			{
				result = true;
			}
			return result;
		}

		private int GetElementConnectorCount()
		{
			int num = 0;
			ConnectorSetIterator csi = this.GetCSI();
			while (csi.MoveNext())
			{
				object obj = csi.Current;
				Connector connector = obj as Connector;
				if ((connector != null && (int)connector.ConnectorType == 1) || (int)connector.ConnectorType == 2 || (int)connector.ConnectorType == 19)
				{
					if ((connector.Domain == 4 || connector.Domain == 1 || connector.Domain == 3) && this.type != Autodesk.Revit.DB.ElementType.Wire)
					{
						num++;
					}
					else if (connector.Domain == 2 && this.type == Autodesk.Revit.DB.ElementType.Wire)
					{
						num++;
					}
				}
			}
			return num;
		}

		public ConnectorSetIterator GetCSI()
		{
			ConnectorSetIterator result = null;
			switch (this.type)
			{
			case Autodesk.Revit.DB.ElementType.Duct:
				result = (this.elem as Duct).ConnectorManager.Connectors.ForwardIterator();
				break;
			case Autodesk.Revit.DB.ElementType.Pipe:
				result = (this.elem as Pipe).ConnectorManager.Connectors.ForwardIterator();
				break;
			case Autodesk.Revit.DB.ElementType.FamilyInstance:
				result = (this.elem as FamilyInstance).MEPModel.ConnectorManager.Connectors.ForwardIterator();
				break;
			case Autodesk.Revit.DB.ElementType.FlexDuct:
				result = (this.elem as FlexDuct).ConnectorManager.Connectors.ForwardIterator();
				break;
			case Autodesk.Revit.DB.ElementType.FlexPipe:
				result = (this.elem as FlexPipe).ConnectorManager.Connectors.ForwardIterator();
				break;
			case Autodesk.Revit.DB.ElementType.Equiment:
				result = (this.elem as FamilyInstance).MEPModel.ConnectorManager.Connectors.ForwardIterator();
				break;
			case Autodesk.Revit.DB.ElementType.AirTerminal:
				result = (this.elem as FamilyInstance).MEPModel.ConnectorManager.Connectors.ForwardIterator();
				break;
			case Autodesk.Revit.DB.ElementType.Sprinklers:
				result = (this.elem as FamilyInstance).MEPModel.ConnectorManager.Connectors.ForwardIterator();
				break;
			case Autodesk.Revit.DB.ElementType.Wire:
				result = (this.elem as Wire).ConnectorManager.Connectors.ForwardIterator();
				break;
			case Autodesk.Revit.DB.ElementType.Conduit:
				result = (this.elem as Conduit).ConnectorManager.Connectors.ForwardIterator();
				break;
			case Autodesk.Revit.DB.ElementType.CableTray:
				result = (this.elem as CableTray).ConnectorManager.Connectors.ForwardIterator();
				break;
			}
			return result;
		}

		public Element Element
		{
			get
			{
				return this.elem;
			}
		}

		public Autodesk.Revit.DB.ElementType Type
		{
			get
			{
				return this.type;
			}
		}

		public Connector ConnectorIn
		{
			get
			{
				return this.connectorIn;
			}
			set
			{
				this.connectorIn = value;
			}
		}

		public Connector ConnectorOut
		{
			get
			{
				return this.connectorOut;
			}
			set
			{
				this.connectorOut = value;
			}
		}

		public IList<Connector> SubConnectors
		{
			get
			{
				return this.subConnectors;
			}
		}

		public IList<ElementNode> SubElementNode
		{
			get
			{
				return this.subElementNode;
			}
		}

		public ElementNode Preview
		{
			get
			{
				return this.preview;
			}
			set
			{
				this.preview = value;
			}
		}

		public ElementNode Next
		{
			get
			{
				return this.next;
			}
			set
			{
				this.next = value;
			}
		}

		public int ElementConnectorCount
		{
			get
			{
				return this.elemConnectorCount;
			}
		}

		private Autodesk.Revit.DB.ElementType CheckType(Element elem)
		{
			Autodesk.Revit.DB.ElementType result;
			if (elem is Duct)
			{
				result = Autodesk.Revit.DB.ElementType.Duct;
			}
			else if (elem is FamilyInstance)
			{
				BuiltInCategory integerValue = elem.Category.Id.IntegerValue;
				if (integerValue == -2001140 || integerValue == -2001160)
				{
					result = Autodesk.Revit.DB.ElementType.Equiment;
				}
				else if (integerValue == -2008099)
				{
					result = Autodesk.Revit.DB.ElementType.Sprinklers;
				}
				else if (integerValue == -2008013)
				{
					result = Autodesk.Revit.DB.ElementType.AirTerminal;
				}
				else
				{
					result = Autodesk.Revit.DB.ElementType.FamilyInstance;
				}
			}
			else if (elem is Pipe)
			{
				result = Autodesk.Revit.DB.ElementType.Pipe;
			}
			else if (elem is FlexDuct)
			{
				result = Autodesk.Revit.DB.ElementType.FlexDuct;
			}
			else if (elem is FlexPipe)
			{
				result = Autodesk.Revit.DB.ElementType.FlexPipe;
			}
			else if (elem is Wire)
			{
				result = Autodesk.Revit.DB.ElementType.Wire;
			}
			else if (elem is Conduit)
			{
				result = Autodesk.Revit.DB.ElementType.Conduit;
			}
			else if (elem is CableTray)
			{
				result = Autodesk.Revit.DB.ElementType.CableTray;
			}
			else
			{
				result = Autodesk.Revit.DB.ElementType.Unknown;
			}
			return result;
		}

		private Element elem;

		private Autodesk.Revit.DB.ElementType type = Autodesk.Revit.DB.ElementType.Unknown;

		private Connector connectorIn;

		private Connector connectorOut;

		private IList<Connector> subConnectors;

		private IList<ElementNode> subElementNode;

		private ElementNode preview;

		private ElementNode next;

		private int elemConnectorCount;
	}
}
